package com.randomrpg.game

import world._
import java.lang.reflect.Method
import java.lang.reflect.Modifier._
import java.util.Random

/**
 * Main game starter
 */
object Game {
  
  /**
   * Dificult level:
   * 1 - easy
   * 2 - normal
   * 3 - hard
   */
  private var difficult = 2
  
  private var ended = false
  private var started = false
  
  private var player: Player = _
  private var currentMap: GameMap = _
  
  private var currentEnemy: Enemy = _
  
  private var pars: Array[String] = _
  
  private val introduction = """
*** Welcome to Random RPG ***

to view the available commands type help

"""
  
  def main(args: Array[String]): Unit = {
    println(introduction)
    while(!ended) {
      print(">")
      Console.flush
      var comm = readLine.split(" ")
      call(comm)
    }
    println("******** CONGRATULATIONS YOU FINISHED THE GAME! ********\n")
    println("player stats:")
    println(player)
  }
  
  private def call(parms: Array[String]) = {
    try {
      this.pars = parms.slice(1, parms.size)
      val method = getClass.getDeclaredMethod(parms(0).trim)
      if(isPublic(method.getModifiers)) method.invoke(this)
    } catch {
      case nsm: NoSuchMethodException => println("invalid command")
      case e: Exception => print("Unknow error! => "); e.printStackTrace
    }
  }
  
  def help = {
    println("game commands:")
    commands foreach println
  }
  
  def exit = {
    if(started) {
      println("do you want to save the game? [y | n]")
      if(readLine.toLowerCase.charAt(0) == 'y') save
    }
    System.exit(0)
  }
  
  def saves = {
    println("Saves:")
    Saver.listSaves.map( s => println("\t" + s))
    println
  }
  
  def save = {
    if((player != null) && (currentMap != null)) {
      Saver.saveGame(player, currentMap)
      println("saved player " + player.name)
    }
  }
  
  def load = {
    var (p, m) = Saver.loadGame(pars(0))
    player = p
    currentMap = m
    println("loaded player " + player.name)
    println
    started = true
    currentEnemy = null
    repaint
  }
  
  def newPlayer = {
    println("choose a name:")
    val name = readLine
    println("choose a class:")
    ClassList.desc.elements.foreach { entry =>
      println("\t" + entry._1 + " - " + entry._2)
    }
    println
    val clazz = readLine
    player = new Player(Class.forName("com.randomrpg.game." + clazz).newInstance.asInstanceOf[RPGClass], name)
    println("you have created a new player: " + name)
    start
  }
  
  def u: Unit = {
    if(started) {
      if(currentMap.up) {
        player.incLife
        player.incMp
        if(checkExit) return
        repaint
        checkEnemy
      } else println("cannot go up")
    }
  }
  
  def d = {
    if(started) {
      if(currentMap.down) {
        player.incLife
        player.incMp
        repaint
        checkEnemy
      } else println("cannot go down")
    }
  }
  
  def l = {
    if(started) {
      if(currentMap.left) {
        player.incLife
        player.incMp
        repaint
        checkEnemy
      } else println("cannot go left")
    }
  }
  
  def r: Unit = {
    if(started) {
      if(currentMap.right) {
        player.incLife
        player.incMp
        if(checkExit) return
        repaint
        checkEnemy
      } else println("cannot go right")
    }
  }
  
  def map = if(currentMap != null) GameMap.printGameMap(currentMap)
  
  def attack = {
    if(currentEnemy != null) {
      verboseAttack(player, currentEnemy)
      checkForDeath(currentEnemy)
      if(currentEnemy != null) enemyAttack
    } else println("no enemy")
  }
  
  def fireball = {
    if(currentEnemy != null) {
      verboseFireball(player, currentEnemy)
      checkForDeath(currentEnemy)
      if(currentEnemy != null) enemyAttack
    } else println("no enemy")
  }
  
  def drain = {
    if(currentEnemy != null) {
      verboseDrain(player, currentEnemy)
      checkForDeath(currentEnemy)
      if(currentEnemy != null) enemyAttack
    } else println("no enemy")
  }
  
  def heal = {
    verboseHeal(player)
    if(currentEnemy != null) enemyAttack
    else repaint
  }
  
  def level = {
    val level = Integer.parseInt(readLine)
    if(level > 0 && level < 4) {
      difficult = level
      println("")
    }
  }
  
  def stats = println(player)
  
  def enemy = if(currentEnemy != null) println(currentEnemy)
  
  private def start = {
    currentMap = GameMap.generateMap(1)
    started = true
    repaint
  }
  
  private def repaint = {
    println(player)
    GameMap.printGameMap(currentMap)
  }
  
  private val rand = new Random
  
  private def checkEnemy: Unit = {      
    var percent = rand.nextInt(100)
    if(percent < (10 * difficult)) {
      currentEnemy = Enemy.getRandom(currentMap.level)
      println("you have encountered " + currentEnemy.name + " level " + currentEnemy.level)
      
      // chance of being attacked first (10 + currentEnemy.level) * difficult
      percent = rand.nextInt(100)
      if(percent < ((5 + currentEnemy.level) * difficult)) {
        enemyAttack
      }
    }
  }
  
  private def enemyAttack = {
    currentEnemy.clazz match {
      case mage: Mage => {
        if(currentEnemy.canCast) {
          if(currentEnemy.lowLife) verboseDrain(currentEnemy, player)
          else verboseFireball(currentEnemy, player)
        } else verboseAttack(currentEnemy, player)
      }
      case cleric: Cleric => {
        if(currentEnemy.canCast && currentEnemy.lowLife) verboseHeal(currentEnemy)
        else verboseAttack(currentEnemy, player)
      }
      case _ => verboseAttack(currentEnemy, player)
    }
    checkForDeath(player)
  }
  
  private def checkForDeath(entity: Player): Unit = {
    val dead = entity.life <= 0
    if(!dead) return
    
	entity match {
	  case e : Enemy => {
	    println(e.name + " is dead")
        val xp = XPTable.giveXP(e.level)
        Thread.sleep(1000)
        println("you gained " + xp + " xp")
	    player.exp += xp
        currentEnemy = null
        Thread.sleep(1000)
        val leveled = player.checkLevelUp
        if(leveled) { println("level up! You are now in level " + player.level); Thread.sleep(1000) }
	  }
	  case _ => {
	    currentEnemy = null
        player = null
        started = false
	    println("you are dead! Load last save? [y | n]")
	    if(readLine.toLowerCase.charAt(0) == 'y') { pars = Array(player.name); load }
	  }
	}
  }
  
  private def checkExit: Boolean = {
    if(currentMap.reachedExit) {
      if(currentMap.level == 20) ended = true
      else {
        currentMap = GameMap.generateMap(currentMap.level + 1)
        println("you go down the stairs to the level " + currentMap.level)
        player.life = player.maxLife
        Thread.sleep(2000)
      }
      true
    }
    false
  }
  
  private def verboseAttack(player: Player, enemy: Player) = {
    println(player.name + " attacks")
    Thread.sleep(1500)
    val (damage, crit) = player.attack(enemy)
    if(damage < 0) println(player.name + " misses")
    else {
      if(crit) println(player.name + " performed a critical hit! " + damage + " damage")
      else println(player.name + " caused " + damage + " damage")
    }
  }
  
  private def verboseDrain(player: Player, enemy: Player) = {
    println(player.name + " cast drain")
    Thread.sleep(1500)
    val (caused, healed) = Magics.drain(player, enemy)
    println(player.name + " stole " + caused + " life from " + enemy.name)
  }
  
  private def verboseFireball(player: Player, enemy: Player) = {
    println(player.name + " cast fireball")
    Thread.sleep(1500)
    val damage = Magics.fireball(player, enemy)
    println(player.name + " caused " + damage + " damage")
  }
  
  private def verboseHeal(player: Player) = {
    println(player.name + " cast heal")
    Thread.sleep(1500)
    val healed = Magics.heal(player)
    println(player.name + " healed " + healed + " life")
    Thread.sleep(1500)
  }
  
  private val commands = "help - show this help" :: "exit - exit the game" ::
    "saves - list save files" :: "save - save current game" ::
    "load *name* - load player *name*" :: "newPlayer - creates a new player" ::
    "level - set difficult level (1 - easy, 2 - normal, 3 - hard)" ::
    "u - go up" :: "d - go down" :: "l - go left" :: "r - go right" ::
    "map - show map" :: "attack - attack enemy" :: "fireball - cast fireball on enemy (Mage only)" ::
    "drain - cast drain on enemy (Mage only)" :: "heal - cast heal (Cleric only)" ::
    "stats - show player stats" :: "enemy - show enemy stats" :: Nil
}